/**
 * 
 */
package info.niwota.webagent.peer;

import info.niwota.org.json.JSONException;
import info.niwota.org.json.JSONObject;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;
import com.google.appengine.api.xmpp.JID;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.MessageBuilder;
import com.google.appengine.api.xmpp.MessageType;
import com.google.appengine.api.xmpp.SendResponse;
import com.google.appengine.api.xmpp.SendResponse.Status;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

/**
 * @author qiangli
 * 
 */
public class ChannelManager {
	private static final Logger log = Logger.getLogger(ChannelManager.class
			.getName());

	private static final ChannelManager instance = new ChannelManager();

	private static final ChannelService channel = ChannelServiceFactory
			.getChannelService();

	private static final XMPPService xmpp = XMPPServiceFactory.getXMPPService();

	private static final String ERROR = "error";

	public static final JID APP_JID = new JID("hotwebproxy@appspot.com/hotweb");

	public static final String TOKEN = "token";

	public static final String CID = "cid";

	private static final String CRLF = "\r\n";

	private static final String SLASH_RESOURCE_HOTWEB = "/hotweb";

	private static long seq = System.currentTimeMillis();

	synchronized private static String nextId(String s) {
		return s + "-" + (++seq);
	}

	private ChannelManager() {
	}

	public static ChannelManager getInstance() {
		return instance;
	}

	public String getChannelKey(String s) {
		return nextId(s == null ? "" : s);
	}

	public void send(String peer, String cid, String tag, String cmd, JSONObject json) throws Exception {
		json = (json == null ? new JSONObject() : json);
		
		json.put("peer", peer);
		json.put("cid", cid);
		json.put("tag", tag);

		String req = cmd + " " + cid + CRLF + encode(json.toString());

		Status st = send(peer, req);

		switch (st) {
		case SUCCESS:
			return;
		case INVALID_ID:
			throw new Exception("Invalid ID");
		case OTHER_ERROR:
		}
		
		throw new Exception("Failed");
	}

	private static Status send(String peer, String message) throws Exception {
		JID jid = new JID(peer+SLASH_RESOURCE_HOTWEB);

		Message msg = new MessageBuilder().withMessageType(MessageType.CHAT)
				.withFromJid(APP_JID).withRecipientJids(jid).withBody(message)
				.build();
		xmpp.sendInvitation(jid);
		SendResponse res = xmpp.sendMessage(msg);
		Status status = res.getStatusMap().get(jid);

		return status;
	}

	private String createChannel(String cid) {

		String ch = channel.createChannel(cid);

		return ch;
	}
	
	private String encode(String s) throws Exception {
		return URLEncoder.encode(s, "UTF-8");
	}

	private String decode(String s) throws Exception {
		return URLDecoder.decode(s, "UTF-8");
	}

	public void dispatch(String peer, String body) {
		try {
			String s = decode(body);

			final int idx = s.indexOf(CRLF);
			String status = s.substring(0, idx);
			String code = status.substring(0, 3); // 2xx
			String cid = status.substring(4);

			String msg = s.substring(idx + CRLF.length());

			JSONObject json = null;

			if (code.charAt(0) == '2') {
				// success
				json = new JSONObject(msg);
			} else {
				json = new JSONObject();
				json.put("error", msg);
			}
			
			json.put("peer", peer);

			log.finest("sending code: " + code + " cid: " + cid + " msg: "
					+ msg);

			dispatch(cid, json);

		} catch (Exception e) {
			log.log(Level.SEVERE, e.getMessage());
		} finally {
			//
		}
	}
	
	public void dispatch(String cid, JSONObject json) {
		channel.sendMessage(new ChannelMessage(cid, json.toString()));
	}

	public JSONObject createChannel(HttpServletRequest req) {
		JSONObject json = new JSONObject();
		try {
			HttpSession sess = req.getSession(true);
			String cid = getChannelKey(sess.getId());
			String token = createChannel(cid);

			json.put(TOKEN, token);
			json.put(CID, cid);
		} catch (Exception e) {
			
			log.log(Level.SEVERE, e.getMessage());
			try {
				json.put(ERROR, e.getLocalizedMessage());
			} catch (JSONException e1) {
				log.log(Level.SEVERE, e1.getMessage());
			}
		}
		return json;
	}
}